package com.wnxy.portal.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wnxy.portal.common.constant.RedisConstant;
import com.wnxy.portal.common.enums.OrderStatusEnum;
import com.wnxy.portal.common.exception.Asserts;
import com.wnxy.portal.common.exception.BusinessException;
import com.wnxy.portal.common.result.impl.BusinessCodeEnum;
import com.wnxy.portal.common.result.impl.ResultCodeEnum;
import com.wnxy.portal.entity.Address;
import com.wnxy.portal.entity.Book;
import com.wnxy.portal.entity.Item;
import com.wnxy.portal.entity.Order;
import com.wnxy.portal.mapper.OrderMapper;
import com.wnxy.portal.model.vo.CartItemVo;
import com.wnxy.portal.model.vo.CartVo;
import com.wnxy.portal.model.vo.OrderItemVo;
import com.wnxy.portal.model.vo.OrderVo;
import com.wnxy.portal.rabbit.config.RabbitConstant;
import com.wnxy.portal.service.IAddressService;
import com.wnxy.portal.service.IBookService;
import com.wnxy.portal.service.IItemService;
import com.wnxy.portal.service.IOrderService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Jet
 * @since 2023-12-11
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {

    @Autowired
    private IItemService itemService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private IAddressService addressService;
    @Autowired
    private IBookService bookService;
    @Autowired
    private RedissonClient redissonClient;

    @Transactional
    @Override
    public void submitOrder(Long userId, Integer addressId, CartVo cartVo) {
        // 【保存订单】
        Order order = new Order();
        // 雪花算法生成订单ID, 使用hutool工具类
        // 参数1：机器id； 参数2：数据中心id
        Snowflake snowflake = IdUtil.getSnowflake(1, 1);
        order.setId(snowflake.nextId());
        // 设置订单编号
        order.setOrderNum("WNXY" + System.currentTimeMillis());
        order.setTotalprice(cartVo.getTotalPrice());
        order.setUserId(userId);
        order.setAddressId(addressId);
        order.setCreatetime(new Date());
        order.setState(OrderStatusEnum.NO_PAY.getCode());
        this.save(order);

        // 【保存订单明细】 GenerateAllSetter
        List<CartItemVo> cartItemVos = cartVo.getCartItemVos();
        cartItemVos.forEach(cartItemVo -> {

            //【Resisson分布式锁：（1） 获取锁对象】
            RLock rLock = redissonClient.getLock("Lock_" + cartItemVo.getId());
            try {
                // 【Resisson分布式锁：（2） 加锁】
                rLock.lock();
                // 判断库存是否充足； 如果库存不足，返回对应提醒
                Integer storeCount = (Integer) redisTemplate.opsForHash().get(RedisConstant.STORAGE, cartItemVo.getId().toString());
                Asserts.error(storeCount < cartItemVo.getBuyCount(), BusinessCodeEnum.VERRIFY_CODE_ERROR);
                // 预扣减库存， 减的是Redis中的库存。 Redis中的库存是预热放入的。
                redisTemplate.opsForHash().increment(RedisConstant.STORAGE, cartItemVo.getId().toString(), -cartItemVo.getBuyCount());
            } finally {
                // 【Resisson分布式锁：（3） 释放锁】
                rLock.unlock();
            }


            // 订单明细对象
            Item item = new Item();
            item.setBookId(cartItemVo.getId());
            item.setBookName(cartItemVo.getName());
            item.setPrice(cartItemVo.getPrice());
            item.setBcount(cartItemVo.getBuyCount());
            item.setSumprice(cartItemVo.getSumPrice());
            item.setOrderId(order.getId()); // 关联订单id
            item.setCreatetime(order.getCreatetime());
            item.setState(order.getState());
            itemService.save(item);
        });

        // 发消息：清空购物车（监听器实现）
        // 消息内容： 用户id、bookId[]
        List<Long> bookIds =
                cartItemVos.stream().map(CartItemVo::getId).collect(Collectors.toList());
        Map<String,Object> contentMap = new HashMap<>();
        contentMap.put("userId",userId);
        contentMap.put("bookIds",bookIds);
        // 对象转json
        String jsonStr = JSONUtil.toJsonStr(contentMap);
        rabbitTemplate.convertAndSend(RabbitConstant.CLEAR_CART_QUEUE,jsonStr);

        // 发送延迟消息，40分钟未支付，自动取消订单：修改订单状态为7（监听器实现）
        // 消息内容：orderId
        rabbitTemplate.convertAndSend("order-exchange","order.key.normal",order.getId().toString());

    }

    @Override
    public List<OrderVo> queryWaitPay(String userId) {
        // 查询待支付的订单：select * from t_order where state=1 and user_id=1
        List<Order> orderList = this.list(Wrappers.lambdaQuery(Order.class)
                .eq(Order::getState, 1)
                .eq(Order::getUserId, userId));


        List<OrderVo> orderVoList = orderList.stream().map(order -> {
            // 封装订单
            OrderVo orderVo = new OrderVo();
            orderVo.setId(order.getId().toString());
            orderVo.setCreatetime(order.getCreatetime());
            orderVo.setOrderNum(order.getOrderNum());
            orderVo.setTotalprice(order.getTotalprice());
            orderVo.setOrderStatus(getOrderEnumString(order.getState()));
            // 根据地址id查询 (抽取一个方法：先从Redis获取，没有获取到再查询MySQL)
            Address address = addressService.getById(order.getAddressId());
            orderVo.setReceiver(address.getReciver());

            // 根据订单id，查询订单明细: select * from t_item where order_id=1
            List<Item> itemList = itemService.list(
                    Wrappers.lambdaQuery(Item.class).eq(Item::getOrderId, order.getId()));

            // 封装订单详情
            List<OrderItemVo> itemVoList = itemList.stream().map(item -> {
                OrderItemVo orderItemVo = BeanUtil.copyProperties(item,OrderItemVo.class);
                // 根据图书id获取图书img (下面是错误示范: 查询优化：尽量避免select *)
                Book book = bookService.getById(item.getBookId());
                orderItemVo.setBookImg(book.getImgsrc());
                return orderItemVo;
            }).collect(Collectors.toList());

            // 设置订单详情
            orderVo.setOrderItem(itemVoList);
            return orderVo;
        }).collect(Collectors.toList());
        return orderVoList;
    }

    @Override
    public void pay(Long orderId) {
        //1. 修改订单状态：待发货
        Order order = this.getById(orderId);
        order.setState(OrderStatusEnum.NO_SEND.getCode());
        this.updateById(order);

        //2. 修改数据库库存
        //2.1 查询订单明细
        List<Item> items = itemService.list(
                Wrappers.lambdaQuery(Item.class).eq(Item::getOrderId, orderId));
        //2.2 遍历：获取每一个商品的购买数量、修改库存
        items.forEach(item -> {
            Book book = bookService.getById(item.getBookId());
            // 修改库存 = 库存 - 购买数量
            book.setStorecount(book.getStorecount() - item.getBcount());
            // 修改购买数量 = 购买数量 + bcount
            book.setBuycount(book.getBuycount() + item.getBcount());
            // 修改
            bookService.updateById(book);
        });
    }

    // 根据编码，获取枚举对应的中文描述
    private String getOrderEnumString(Integer status) {
        // OrderStatusEnum.values() 获取所有的枚举对象
        for (OrderStatusEnum value : OrderStatusEnum.values()) {
            if (value.getCode() == status) {
                return value.getDescribe();
            }
        }
        throw new BusinessException(ResultCodeEnum.FAIL);
    }
}
